<?php
/**
 * 21cnjySDK
 * @author    Simon <150986416@qq.com>
 * @filename  Client.php
 * @created   2017/7/5 上午8:57
 */

namespace sdk;

use sdk\modules\Common;
use sdk\modules\Document;
use sdk\modules\Question;
use sdk\modules\Video;

/**
 * SDK HttpClient 客户端调用调度类
 * 用于发起调用http请求以及指定调用指定模块下的接口资源
 * Class Client
 * @package sdk
 */
class Client
{
    /**
     * @var string 21教育开放平台开发者 AccessKey
     */
    public $accessKey;

    /**
     * @var string 21教育开放平台开发者 AccessSecret
     */
    public $accessSecret;

    /**
     * @var string 21教育开放平台接口域名地址,必须以 http:// 或 https:// 开头
     */
    public $baseUrl;

    /**
     * @var object 当前类单例对象
     */
    protected static $instance;


    private function __construct($properties)
    {
        if (!empty($properties)) {
            foreach ($properties as $property => $value) {
                $this->{$property} = $value;
            }
        }
    }

    /**
     * 得到单例对象
     * @param $config
     * @return object|Client
     */
    public static function getInstance($config)
    {
        if (empty(self::$instance) || !self::$instance instanceof Client) {
            self::$instance = new self($config);
        }

        return self::$instance;
    }

    /**
     * 获取公共接口服务实例
     * @return Common
     */
    public function getCommonService()
    {
        return (new Common($this));
    }

    /**
     * 获取题库资源接口服务实例
     * @return Question
     */
    public function getQuestionService()
    {
        return (new Question($this));
    }

    /**
     * 获取视频资源接口服务实例
     * @return Video
     */
    public function getVideoService()
    {
        return (new Video($this));
    }

    /**
     * 获取文档资源接口服务实例
     * @return Document
     */
    public function getDocumentService()
    {
        return (new Document($this));
    }

    /**
     * 得到签名加工后的请求参数
     * @param $params
     * @return mixed
     */
    protected function getSignatureParams($params)
    {
        if (array_key_exists(SignatureHelper::CLIENT_SIGN_PARAM, $params)) {
            unset($params['sign']);
        }

        $params[SignatureHelper::CLIENT_TIMESTAMP_PARAM] = time();
        $params[SignatureHelper::CLIENT_SALT_PARAM] = $this->random();
        $params[SignatureHelper::CLIENT_ACCESS_KEY_PARAM] = trim($this->accessKey);
        $params[SignatureHelper::CLIENT_SIGN_PARAM] = trim(SignatureHelper::generator($params, $this->accessSecret));

        return $params;
    }

    /**
     *  创建一个随机字符串
     * @param   string $type 随机字符类型
     * @param   int $length 指定随机字符串长度
     * @return  string  the random string
     */
    public function random($type = 'alnum', $length = 16)
    {
        switch ($type) {
            case 'basic':
                return mt_rand();
                break;

            default:
            case 'alnum':
            case 'numeric':
            case 'nozero':
            case 'alpha':
            case 'distinct':
            case 'hexdec':
                switch ($type) {
                    case 'alpha':
                        $pool = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
                        break;

                    default:
                    case 'alnum':
                        $pool = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
                        break;

                    case 'numeric':
                        $pool = '0123456789';
                        break;

                    case 'nozero':
                        $pool = '123456789';
                        break;

                    case 'distinct':
                        $pool = '2345679ACDEFHJKLMNPRSTUVWXYZ';
                        break;

                    case 'hexdec':
                        $pool = '0123456789abcdef';
                        break;
                }

                $str = '';
                for ($i = 0; $i < $length; $i++) {
                    $str .= substr($pool, mt_rand(0, strlen($pool) - 1), 1);
                }
                return $str;
                break;

            case 'unique':
                return md5(uniqid(mt_rand()));
                break;

            case 'sha1' :
                return sha1(uniqid(mt_rand(), true));
                break;
        }
    }

    /**
     * 发起 Http GET 请求
     * @param string $url 请求的地址
     * @param array $params 请求参数
     * @return bool|mixed
     */
    public function httpGet($url, $params)
    {
        return $this->request($url, $this->getSignatureParams($params), 'get');
    }

    /**
     * 发起 Http Post 请求
     * @param string $url 请求地址
     * @param array $params 请求参数
     * @return bool|mixed
     */
    public function httpPost($url, $params)
    {
        return $this->request($url, $this->getSignatureParams($params), 'post');
    }

    /**
     * 发起 Http Raw 请求
     * 该请求方式用于提交 JSON 数据的请求，提交的参数会被转成JSON格式
     * @param $url
     * @param $params
     * @return bool|mixed
     */
    public function httpRaw($url, $params)
    {
        return $this->request($url, json_encode($this->getSignatureParams($params)), 'raw');
    }

    /**
     * 发起 CURL 请求
     * @param string $url 请求的服务地址
     * @param array|string $params 请求的提交的参数
     * @param string $method 请求方式
     * @return bool|mixed
     * @throws HttpException
     */
    protected function request($url, $params = null, $method = 'get')
    {
        if (stripos($url, 'http://') === false && stripos($url, 'https://') === false) {
            $url = $this->baseUrl . $url;
        }

        $curl = curl_init();
        switch ($method) {
            case 'get':
                is_array($params) && $params = http_build_query($params);
                !empty($params) && $url .= (stripos($url, '?') === false ? '?' : '&') . $params;
                //var_dump($url);exit;
                break;
            case 'post':
                curl_setopt($curl, CURLOPT_POST, true);
                if (!is_array($params)) {
                    throw new HttpException("Post data must be an array.");
                }
                curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
                break;
            case 'raw':
                curl_setopt($curl, CURLOPT_POST, true);
                if (is_array($params)) {
                    throw new HttpException("Post raw data must not be an array.");
                }
                curl_setopt($curl, CURLOPT_POSTFIELDS, $params);
                break;
            default:
                throw new HttpException("Invalid http method '{$method}.' called.");
        }

        if (stripos($url, "https://") !== false) {
            curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false);
            curl_setopt($curl, CURLOPT_SSL_VERIFYHOST, false);
            curl_setopt($curl, CURLOPT_SSLVERSION, CURL_SSLVERSION_TLSv1);
        }

        curl_setopt($curl, CURLOPT_URL, $url);
        curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1);
        $content = curl_exec($curl);
        $status = curl_getinfo($curl);
        curl_close($curl);
        if (isset($status['http_code']) && intval($status['http_code']) == 200) {
            return $this->parseHttpResponse($content);
        }

        return false;
    }

    /**
     * 解析 Http 请求响应
     * @param $response
     * @return mixed
     */
    protected function parseHttpResponse($response)
    {
        return json_decode($response);
    }
}